﻿using Microsoft.Scripting.Utils;
using Scala.Compiler.Binding;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;

namespace Scala.Compiler.Ast
{
    public class SelectStatement : Expression
    {
        private readonly Expression _qual;
        private readonly Expression _name;

        public SelectStatement(Expression qual, Expression name)
        {
            ContractUtils.RequiresNotNull(qual, "qual");
            ContractUtils.RequiresNotNull(name, "name");

            _qual = qual;
            _name = name;
        }

        public Expression Qual { get { return _qual; } }
        public Expression ExprName { get { return _name; } }
        public MethodInfo Meth { get; set; }
        public PropertyBuilder Prop { get; set; }
        public PropertyBuilder RetProp { get; set; }
        public ScalaType ScalaType { get; set; }
        public Microsoft.Scripting.Ast.Expression QualExpr { get; set; }
        public Type dotNetType{get;set;}
        public bool IsProp { get; set; }
        public bool IsMeth { get; set; }
        public bool IsType { get; set; }

        public override void Walk(ScalaWalker walker)
        {
            if (walker.Walk(this))
            {
                if (_qual != null)
                {
                    _qual.Walk(walker);
                }
                if (_name != null)
                {
                    _name.Walk(walker);
                }
            }
            walker.PostWalk(this);
        }

        public override Microsoft.Scripting.Ast.Expression Reduce()
        {
            ScalaType scType = null, retType = null;
            ScalaFunction func;
            ScalaVariable prop;

            if (Qual is SelectStatement)
            {
                Qual.Reduce();
                scType = (Qual as SelectStatement).ScalaType;
            }

            if (Qual is NameExpression)
            {
                var t = Qual as NameExpression;
                QualExpr = t.Reduce();
                if (QualExpr == null)
                {
                    if (t.PropInfo != null) Prop = t.PropInfo;
                }
                if (!Scope.TryGetAnyType(t.TypeName, out scType))
                {
                    throw new Exception(String.Format("Not found type {0} in the Scope {1}", t.Name, Scope));
                }
            }

            if (!(ExprName is NameExpression))
            {
                throw new Exception(String.Format("Not found the member {0} of the type {1}", ExprName.ToString(), Qual.ToString()));
            }

            if (scType.ScalaScope.TryGetFunc((ExprName as NameExpression).Name, out func))
            {
                Meth = func.MethodBuilder;
                IsMeth = true;
            }
            else if (scType.ScalaScope.TryGetVariable((ExprName as NameExpression).Name, out prop))
            {
                if(!prop.IsProperty)
                {
                    throw new Exception(String.Format("Invalid Access of the variable {0} in the Type {1}", prop.Name, scType.Name));
                }
                RetProp = prop.PropertyBuilder;
                IsProp = true;
            }
            else if (scType.ScalaScope.TryGetType((ExprName as NameExpression).Name, out retType))
            {
                ScalaType = retType;
                IsType = true;
            }
            //if (func.MethodBuilder == null)
            //{
            //    throw new Exception(String.Format("Not found the member function {0} of the Type {1}", func.Name, scType.Name));
            //}
            return Expression.Empty();
        }
    }
}
